package com.hanxiaozhang.sourcecode.base;

/**
 * 〈一句话功能简述〉<br>
 * 〈〉
 *
 * @author hanxinghua
 * @create 2023/6/13
 * @since 1.0.0
 */
public class MyInteger {


    public static void main(String[] args) {

        System.out.println(parseInt("123", 4));
        System.out.println(toString(-27, 4));

    }


    final static char[] digits = {
            '0', '1', '2', '3', '4', '5',
            '6', '7', '8', '9', 'a', 'b',
            'c', 'd', 'e', 'f', 'g', 'h',
            'i', 'j', 'k', 'l', 'm', 'n',
            'o', 'p', 'q', 'r', 's', 't',
            'u', 'v', 'w', 'x', 'y', 'z'
    };


    /**
     * 根据传入进制（radix），将字符串转换成十进制数字
     *
     * @param s
     * @param radix [2,36]之间的进制
     * @return
     * @throws NumberFormatException
     */
    public static int parseInt(String s, int radix) throws NumberFormatException {

        // 判断s是否为null
        if (s == null) {
            throw new NumberFormatException("null");
        }
        // 判断进制是否在2~36之间，比如10进制的数字，radix就填写10
        if (radix < Character.MIN_RADIX) {
            throw new NumberFormatException("radix " + radix + " less than Character.MIN_RADIX");
        }
        if (radix > Character.MAX_RADIX) {
            throw new NumberFormatException("radix " + radix + " greater than Character.MAX_RADIX");
        }


        // 返回结果的负数值
        int result = 0;
        // 判断是否为负数，false 代表正数，true 代表负数
        boolean negative = false;
        int i = 0, len = s.length();
        // limit 默认初始化为 最大正整数的负数 ，作用：判断是否溢出
        int limit = -Integer.MAX_VALUE;
        // 判断当前的result在接受下一个字符串位置的数字后，会不会溢出
        int multmin;
        // 根据i指针获取到每一个数值，存储到digit中
        int digit;
        if (len > 0) {
            // 取出字符串第一个字符
            char firstChar = s.charAt(0);
            // 判断第一个字符不是数字，可能是"+" 或 "-"
            if (firstChar < '0') {
                if (firstChar == '-') {
                    // '-'时，如下设置
                    negative = true;
                    limit = Integer.MIN_VALUE;
                } else if (firstChar != '+') {
                    // 不是'-'，并且不是'+'时，抛异常
                    throw new NumberFormatException("For input string: \"" + s + "\"");
                }
                // 字符串单独只有"+" 或 "-"时，抛异常
                if (len == 1) {
                    throw new NumberFormatException("For input string: \"" + s + "\"");
                }
                // 进行下一个元素判断
                i++;
            }
            // 判断当前的result在接受下一个字符串位置的数字后会不会溢出
            multmin = limit / radix;
            while (i < len) {
                /**
                 * 做一个假设，s = 123 radix = 4
                 * i=1  digit=1 -> result *= radix  -->  result = 0*4 = 0 ;  result -= digit --> result = 0-1 = -1
                 * i=2  digit=2 -> result *= radix --> result = -1*4 = -4; result -= digit --> result = -4-2 = -6
                 * i=2  digit=3 -> result *= radix --> result =  -6*4 = -24; result -= digit --> result = -24-3 = -27
                 * 此时，negative = false，则返回 -result，即最终结果为：27
                 *
                 * */

                // 返回符合该进制的有效数字，不符合返回-1
                digit = Character.digit(s.charAt(i++), radix);
                // digit小于0时，抛异常
                if (digit < 0) {
                    throw new NumberFormatException("For input string: \"" + s + "\"");
                }
                // 结果 小于 最小值溢出 时，抛异常
                if (result < multmin) {
                    throw new NumberFormatException("For input string: \"" + s + "\"");
                }
                // result = result*radix
                result *= radix;
                if (result < limit + digit) {
                    throw new NumberFormatException("For input string: \"" + s + "\"");
                }
                // result =result-digit
                result -= digit;
            }
        } else {
            // 字符串长度小于0，抛异常
            throw new NumberFormatException("For input string: \"" + s + "\"");
        }
        return negative ? result : -result;

    }


    /**
     * 根据传入进制（radix），将十进制数字换成相应进制的字符串转。
     * <p>
     * 使用短除法
     * <p>
     * 为什么是需要转换成负值？
     * 如果不转换为负值，while (i <= -radix) -> while (i >= radix)。
     * 传入负值时，不会进入该循环。算出来的值  也不对。
     *
     * @param i
     * @param radix
     * @return
     */
    public static String toString(int i, int radix) {

        // 判断radix范围 [2,36]
        if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
            radix = 10;
        }
        // 使用更快的版本
        if (radix == 10) {
            return Integer.toString(i);
        }

        char buf[] = new char[33];
        // 判断是否为负数
        boolean negative = (i < 0);
        int charPos = 32;

        // 正数时，将i设置成负数
        if (!negative) {
            i = -i;
        }
        // i < - radix
        while (i <= -radix) {
            // i % radix -> 取余，现在是负值
            // -(i % radix) -> 取余，正值
            // digits[-(i % radix)] -> 获取digits数组中char
            // buf[charPos] 赋值digits数组中char
            // charPos--
            buf[charPos--] = digits[-(i % radix)];
            // 获取下一次的i
            i = i / radix;
        }
        // 赋值
        buf[charPos] = digits[-i];

        // 如果是负数，添加符号未
        if (negative) {
            // 先减一，再赋值
            buf[--charPos] = '-';
        }

        // 构建String
        return new String(buf, charPos, (33 - charPos));
    }


}
